﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using Microsoft.SharePoint;
using Kiiro.Common.Entities;
using Kiiro.Common.Utility;
using Kiiro.ExceptionManagement;

using UserFieldNames = Kiiro.Common.Utility.Constants.UserFieldNames;
using SPListNames = Kiiro.Common.Utility.Constants.SPListNames;

namespace Kiiro.DataAccess
{
    public class UserDA
    {
        #region Singleton
        private UserDA ( )
        {
        }

        private static UserDA instance = null;
        private static readonly object threadLock = new object ( );

        public static UserDA Instance
        {
            get
            {
                lock ( threadLock )
                {
                    if ( instance == null )
                    {
                        instance = new UserDA ( );
                    }
                }
                return instance;
            }
        }
        #endregion

        /// <summary>
        /// Gets a list of active, not deleted, and not sharepoint account User entities
        /// </summary>
        /// <param name="userIds">The list of user ids to get.</param>
        /// <param name="siteCollectionId">The site collection id</param>
        /// <param name="serverRelativeUrl">The server relative url</param>
        public List<User> GetUsersByIds ( List<int> userIds, Guid siteCollectionId, string serverRelativeUrl )
        {
            List<User> users = new List<User> ( );
            SPList list = null;

            using ( SPWeb web = SPHelpers.GetPortalWeb ( siteCollectionId, serverRelativeUrl ) )
            {
                list = web.Lists [ SPListNames.Users ];

                if ( list != null )
                {
                    SPQuery query = new SPQuery ( );

                    bool first = true;
                    if ( userIds != null && userIds.Count > 0 )
                    {
                        foreach ( int userId in userIds )
                        {
                            if ( !first ) query.Query = "<Or>" + query.Query;
                            query.Query += "<Eq><FieldRef Name='ID'/><Value Type='Integer'>" + userId + "</Value></Eq>";
                            if ( !first ) query.Query += "</Or>";

                            first = false;
                        }
                    }

                    if ( !string.IsNullOrEmpty ( query.Query ) )
                        query.Query = "<Where>" + query.Query + "</Where>";

                    SPListItemCollection listItems = list.GetItems ( query );
                    if ( listItems != null )
                        foreach ( SPListItem listItem in listItems )
                            users.Add ( SPDataHelpers.MakeUser ( listItem, siteCollectionId, serverRelativeUrl ) );
                }
            }
            return users;
        }

        public User GetUserByLogin ( string login, Guid siteCollectionId, string serverRelativeUrl )
        {
            User user = null;

            try
            {
                login = login.Replace ( "kiiromembership:", "" );

                if ( login.ToLower ( ) == "sharepoint\\system" )
                    login = System.Security.Principal.WindowsIdentity.GetCurrent ( ).Name;

                using ( SPWeb web = SPHelpers.GetPortalWeb ( siteCollectionId, serverRelativeUrl ) )
                {
                    SPList list = web.Lists [ SPListNames.Users ];

                    if ( list != null )
                    {
                        string queryText = "<Where><Eq><FieldRef Name='{0}'/><Value Type='Text'>{1}</Value></Eq></Where>";
                        SPQuery query = new SPQuery { Query = string.Format ( queryText, UserFieldNames.Login, login ), RowLimit = 1 };

                        SPListItemCollection listItems = list.GetItems ( query );
                        foreach ( SPListItem listItem in listItems )
                            user = SPDataHelpers.MakeUser ( listItem, siteCollectionId, serverRelativeUrl );
                    }
                }
            }
            catch ( Exception ex )
            {
                ExceptionManager.HandleException ( ex, true );
            }

            return user;
        }

        public User GetUserByEmail ( string email, Guid siteCollectionId, string serverRelativeUrl )
        {
            User user = null;

            try
            {
                using ( SPWeb web = SPHelpers.GetPortalWeb ( siteCollectionId, serverRelativeUrl ) )
                {
                    SPList list = web.Lists [ SPListNames.Users ];

                    if ( list != null )
                    {
                        string queryText = "<Where><Eq><FieldRef Name='{0}'/><Value Type='Text'>{1}</Value></Eq></Where>";
                        SPQuery query = new SPQuery { Query = string.Format ( queryText, UserFieldNames.Email, email ), RowLimit = 1 };

                        SPListItemCollection listItems = list.GetItems ( query );
                        foreach ( SPListItem listItem in listItems )
                            user = SPDataHelpers.MakeUser ( listItem, siteCollectionId, serverRelativeUrl );
                    }
                }
            }
            catch ( Exception ex )
            {
                ExceptionManager.HandleException ( ex, true );
            }

            return user;
        }

        public List<User> GetAddToAllProjectsUsers ( Guid siteCollectionId, string serverRelativeUrl )
        {
            List<User> users = new List<User> ( );

            try
            {
                using ( SPWeb web = SPHelpers.GetPortalWeb ( siteCollectionId, serverRelativeUrl ) )
                {
                    SPList list = web.Lists [ SPListNames.Users ];

                    if ( list != null )
                    {
                        string queryText = "<Where><Eq><FieldRef Name='{0}'/><Value Type='Boolean'>1</Value></Eq></Where>";
                        SPQuery query = new SPQuery { Query = string.Format ( queryText, UserFieldNames.AddToAllProjects ) };

                        SPListItemCollection listItems = list.GetItems ( query );
                        foreach ( SPListItem listItem in listItems )
                            users.Add ( SPDataHelpers.MakeUser ( listItem, siteCollectionId, serverRelativeUrl ) );
                    }
                }
            }
            catch ( Exception ex )
            {
                ExceptionManager.HandleException ( ex, true );
            }

            return users;
        }

        public User UpdateStatusMessage ( User user, Guid siteCollectionId, string serverRelativeUrl )
        {
            try
            {
                using ( SPWeb web = SPHelpers.GetPortalWeb ( siteCollectionId, serverRelativeUrl ) )
                {
                    SPList list = web.Lists [ Constants.SPListNames.Users ];

                    if ( list != null && user.EditType == EditType.Updated )
                    {
                        SPListItem listItem = list.GetItemById ( user.Id );

                        listItem [ UserFieldNames.ModifiedBy ] = user.Id;
                        listItem [ UserFieldNames.ModifiedDate ] = DateTime.Now;
                        listItem [ UserFieldNames.StatusNote ] = !string.IsNullOrEmpty ( user.StatusNote ) ? user.StatusNote.Trim ( ) : "";

                        web.AllowUnsafeUpdates = true;
                        listItem.UpdateOverwriteVersion ( );
                        web.AllowUnsafeUpdates = false;
                    }
                }
            }
            catch ( Exception ex )
            {
                ExceptionManager.HandleException ( ex, true );
            }

            return user;
        }

        public User ProvisionerSave ( User user, Guid siteCollectionId, string serverRelativeUrl )
        {
            using ( SPWeb web = SPHelpers.GetSystemWeb ( siteCollectionId, serverRelativeUrl ) )
            {
                using ( SPWeb portalWeb = SPHelpers.IsProject ( web ) ? web.ParentWeb : web )
                {
                    return Save ( user, portalWeb, siteCollectionId, serverRelativeUrl );
                }
            }
        }

        public User Save ( User user, Guid siteCollectionId, string serverRelativeUrl )
        {
            using ( SPWeb web = SPHelpers.GetPortalWeb ( siteCollectionId, serverRelativeUrl ) )
            {
                return Save ( user, web, siteCollectionId, serverRelativeUrl );
            }
        }

        // The SPWeb must be passed as a parameter because the ProvisionerSave requires an elevated web
        private User Save ( User user, SPWeb web, Guid siteCollectionId, string serverRelativeUrl )
        {
            SPListItem listItem = null;

            try
            {
                SPList list = web.Lists [ SPListNames.Users ];
                if ( user == null )
                {
                    throw new Exception ( "Cannot SaveUser() when User entity is null" );
                }

                if ( list != null )
                {
                    listItem = user.EditType == EditType.Created ? list.Folders.Add ( ) : list.Folders.GetItemById ( user.Id );

                    #region Set Item Properties

                    if ( listItem != null && user.EditType != EditType.Deleted )
                    {
                        // the double question mark operator '??' will 
                        //      use the first value if it is not null else use the second value
                        listItem [ UserFieldNames.AddToAllProjects ] = user.AddToAllProjects;
                        listItem [ UserFieldNames.CompanyName ] = user.CompanyName ?? string.Empty;
                        listItem [ UserFieldNames.Email ] = user.Email ?? string.Empty;
                        listItem [ UserFieldNames.FirstName ] = user.FirstName ?? string.Empty;
                        listItem [ UserFieldNames.IsADGroup ] = user.IsADGroup;
                        listItem [ UserFieldNames.IM ] = user.IM;
                        listItem [ UserFieldNames.IMClient ] = user.IMClient;
                        listItem [ UserFieldNames.IsNonExplicitMember ] = user.IsNonExplicitMember;
                        listItem [ UserFieldNames.IsFormsUser ] = user.IsFormsUser;
                        listItem [ UserFieldNames.IsSharePointAccount ] = user.IsSharePointAccount;
                        listItem [ UserFieldNames.LastName ] = user.LastName ?? string.Empty;
                        listItem [ UserFieldNames.Login ] = user.Login ?? string.Empty;
                        listItem [ UserFieldNames.ManageProjects ] = user.ManageProjectsOnly;
                        listItem [ UserFieldNames.PasswordSecurityQuestion ] = user.PasswordSecurityQuestion;
                        listItem [ UserFieldNames.PhoneNumber ] = user.PhoneNumber ?? string.Empty;
                        listItem [ UserFieldNames.ProjectIds ] = FormattingHelpers.ListOfIntsToFieldText ( user.ProjectIds );
                        listItem [ UserFieldNames.ReceiveEmailNotification ] = user.ReceiveEmailNotification;
                        listItem [ UserFieldNames.SPUserId ] = user.SPUserId;
                        listItem [ UserFieldNames.StatusNote ] = user.StatusNote ?? string.Empty;
                        listItem [ UserFieldNames.Title ] = user.Title ?? string.Empty;

                        SPDataHelpers.PopulateCustomFieldsInListItemFromEntity ( listItem, user );
                    }
                    #endregion

                    #region Create/Update Fields
                    switch ( user.EditType )
                    {
                        case EditType.Created:
                            listItem.Web.AllowUnsafeUpdates = true;
                            listItem.Update ( );
                            listItem.Web.AllowUnsafeUpdates = false;

                            listItem = list.Folders.GetItemById ( listItem.ID );
                            listItem [ UserFieldNames.ServerRelativeUrl ] = listItem.Folder.ServerRelativeUrl;
                            listItem [ UserFieldNames.SiteCollectionId ] = listItem.Web.Site.ID;

                            if ( user.CreatedBy != null && user.CreatedBy.Id > 0 )
                            {
                                listItem [ UserFieldNames.CreatedBy ] = user.CreatedBy.Id;
                            }

                            if ( user.CreatedDate != null && user.CreatedDate != DateTime.MinValue )
                            {
                                listItem [ UserFieldNames.CreatedDate ] = user.CreatedDate;
                            }

                            if ( user.ModifiedBy != null && user.ModifiedBy.Id > 0 )
                            {
                                listItem [ UserFieldNames.ModifiedBy ] = user.ModifiedBy.Id;
                            }

                            if ( user.ModifiedDate != null && user.ModifiedDate != DateTime.MinValue )
                            {
                                listItem [ UserFieldNames.ModifiedDate ] = user.ModifiedDate;
                            }
                            break;

                        case EditType.Updated:

                            listItem [ UserFieldNames.ServerRelativeUrl ] = listItem.Folder.ServerRelativeUrl;
                            listItem [ UserFieldNames.SiteCollectionId ] = listItem.Web.Site.ID;
                            if ( user.ModifiedBy != null && user.ModifiedBy.Id > 0 )
                            {
                                listItem [ UserFieldNames.ModifiedBy ] = user.ModifiedBy.Id;
                            }

                            if ( user.ModifiedDate != null && user.ModifiedDate != DateTime.MinValue )
                            {
                                listItem [ UserFieldNames.ModifiedDate ] = user.ModifiedDate;
                            }
                            break;

                        case EditType.Deleted:
                        default:
                            break;
                    }
                    #endregion

                    #region Password

                    if ( user.IsFormsUser )
                    {
                        if ( !string.IsNullOrEmpty ( user.Password ) )
                        {
                            if ( listItem.Properties [ UserFieldNames.Password ] == null )
                            {
                                listItem.Properties.Add ( UserFieldNames.Password, user.Password );
                            }
                            else
                            {
                                listItem.Properties [ UserFieldNames.Password ] = user.Password;
                            }
                        }

                        if ( !string.IsNullOrEmpty ( user.PasswordSecurityAnswer ) )
                        {
                            if ( listItem.Properties [ UserFieldNames.PasswordSecurityAnswer ] == null )
                            {
                                listItem.Properties.Add ( UserFieldNames.PasswordSecurityAnswer, user.PasswordSecurityAnswer );
                            }
                            else
                            {
                                listItem.Properties [ UserFieldNames.PasswordSecurityAnswer ] = user.PasswordSecurityAnswer;
                            }
                        }
                    }

                    listItem [ UserFieldNames.PasswordSecurityQuestion ] = user.PasswordSecurityQuestion ?? string.Empty;

                    #endregion

                    #region Picture

                    if ( listItem != null && user.EditType != EditType.Deleted )
                    {
                        try
                        {
                            if ( !string.IsNullOrEmpty ( user.PictureUrl ) )
                            {
                                // need to use SystemWeb to upload picture, FogBugz #295
                                using ( SPWeb systemWeb = SPHelpers.GetSystemWeb ( siteCollectionId, serverRelativeUrl ) )
                                {
                                    using ( SPWeb portalWeb = SPHelpers.IsProject ( systemWeb ) ? systemWeb.ParentWeb : systemWeb )
                                    {
                                        if ( listItem.Folder.Exists && user.PictureBytes != null )
                                        {
                                            SPFolder folder = portalWeb.GetFolder ( listItem.Folder.UniqueId );
                                            string pictureUrl = string.Format ( "{0}/{1}", folder.ServerRelativeUrl, user.PictureUrl );
                                            listItem [ UserFieldNames.PictureUrl ] = pictureUrl;

                                            portalWeb.AllowUnsafeUpdates = true;
                                            folder.Files.Add ( pictureUrl, user.PictureBytes, true );
                                            portalWeb.AllowUnsafeUpdates = false;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                listItem [ UserFieldNames.PictureUrl ] = null;
                            }
                        }
                        catch ( Exception ex )
                        {
                            ExceptionManager.HandleException ( ex, true );
                        }
                    }

                    #endregion

                    #region Update / Delete Item
                    try
                    {
                        listItem.Web.AllowUnsafeUpdates = true;

                        if ( user.EditType == EditType.Deleted )
                        {
                            listItem [ UserFieldNames.ProjectIds ] = string.Empty;
                            listItem.Update ( );
                        }
                        else
                        {
                            listItem.Update ( );
                            user = SPDataHelpers.MakeUser ( listItem, siteCollectionId, serverRelativeUrl );
                        }
                    }
                    catch ( Exception ex )
                    {
                        ExceptionManager.HandleException ( ex, true );
                    }
                    finally
                    {
                        listItem.Web.AllowUnsafeUpdates = false;
                    }
                    #endregion

                }
            }
            catch ( Exception ex )
            {
                ExceptionManager.HandleException ( ex, true );
            }

            return user;
        }
    }
}

/*************************************************************************
 * 
 * $Log: /Internal Projects/Kiiro/Kiiro.DataAccess/UserDA.cs $
 * 
 * 38    2/08/10 5:04p Camerons
 * 
 * 37    1/25/10 1:17p Camerons
 * 
 * 36    1/12/10 12:02p Camerons
 * 
 * 35    26/11/09 10:35a Camerons
 * 
 * 34    17/11/09 6:46p Camerons
 * 
 * 33    7/11/09 2:41p Camerons
 * 
 * 32    29/10/09 4:44p Camerons
 * 
 * 31    28/10/09 12:00p Camerons
 * 
 * 30    28/10/09 11:01a Camerons
 * 
 * 29    10/26/09 3:11p Adamw
 * 
 * 28    10/26/09 2:13p Adamw
 * Case 295, elevated uploading of user picture
 * 
 * 27    7/10/09 5:14p Camerons
 * Case #281, #285: Update SPDataHelpers.MakeUser to take SiteCollectionId
 * and ServerRelativeUrl as params. Used to properly determine the group
 * memberships.
 * 
 * 26    7/10/09 2:38p Camerons
 * Case #283: When deleting user, clear the User.ProjectIds and remove the
 * SPUser from any Kiiro SPGroups
 * 
 * 25    5/10/09 11:07a Camerons
 * RE: Create user on demand, checks for HasUnlimitedUser before creating
 * 
 * 24    1/10/09 3:28p Camerons
 * Load Expanded ADGroups into drop down on NewTeamMember. Create user on
 * demand if selected user doesn't exist.
 * 
 * 23    30/09/09 1:08p Camerons
 * Implementing UserModel.GetOrCreateUser(). 
 * 
 * 22    29/09/09 1:22p Camerons
 * Implementing User.SPUserId
 * 
 * 21    28/09/09 3:38p Camerons
 * 
 * 20    23/09/09 4:46p Camerons
 * Fixed some old code dealing with User.ManageProjects
 * 
 * 19    22/09/09 6:03p Camerons
 * Code changes to support creating user on demand - version 1.1.x.x
 * 
 * 18    21/09/09 3:30p Camerons
 * First batch of changes for 1.1.x.x - Security groups update
 * 
 * 17    10/09/09 7:15p Camerons
 * 
 * 16    3/09/09 1:22p Camerons
 * Added overloaded Save method for PortalProvider
 * 
 * 15    17/08/09 10:41a Camerons
 * Added check-in comments section
 * 
 ************************************************************************/